<template>
  <n-modal
    v-model:show="showModal"
    :show-icon="false"
    preset="dialog"
    title="选择文件"
    :style="{ width: props.width + 'px' }"
    @update:show="handleUpdateShow"
  >
    <n-grid :x-gap="15">
      <n-grid-item span="6" style="display: flex; flex-direction: column">
        <div class="side-head-box">
          <n-button text type="primary" @click="handleFileCgyAdd(null)">
            <template #icon>
              <n-icon><PlusOutlined /></n-icon>
            </template>
            新增类目
          </n-button>
        </div>
        <div class="side-body-box">
          <n-tree
            block-line
            :data="fileCgyList"
            key-field="fileCgyId"
            label-field="fileCgyNm"
            selectable
            :render-suffix="renderFileCgyTreeSuffix"
            @update:selected-keys="handleCgySelectChange"
          />
          <div v-if="fileCgyloading" class="load-mask">
            <n-spin class="spin" :show="true" />
          </div>
        </div>
      </n-grid-item>
      <n-grid-item span="18" style="display: flex; flex-direction: column; gap: 10px">
        <div class="main-head-box">
          <n-space>
            <n-button type="success" :loading="upFileloading" @click="handleUpload">
              <template #icon>
                <n-icon><CloudUploadOutlined /></n-icon>
              </template>
              上传
            </n-button>
            <n-button @click="handleClickOpFile">操作</n-button>
            <n-button v-if="isOperateFile" type="warning" :loading="moveBatchLoading" @click="handleClickBatchMove">
              批量移动{{ operateFileIds.length > 0 ? `(${operateFileIds.length})` : '' }}
            </n-button>
            <n-popconfirm v-if="isOperateFile" @positive-click="handleClickBatchDelFile">
              <template #trigger>
                <n-button :loading="delBatchLoading" :disabled="delBatchLoading" type="error"
                  >批量删除{{ operateFileIds.length > 0 ? `(${operateFileIds.length})` : '' }}</n-button
                >
              </template>
              是否确认删除当前选中的{{ operateFileIds.length }}个文件？
            </n-popconfirm>
          </n-space>
          <n-space>
            <n-input type="text" v-model:value="formParams.keyword" placeholder="请输入关键字查询" />
            <n-button type="primary" :loading="dataLoading" @click="handleSearch">
              <template #icon>
                <n-icon><SearchOutlined /></n-icon>
              </template>
              查询
            </n-button>
            <n-button @click="handleReset">重置</n-button>
          </n-space>
        </div>
        <div class="main-body-box">
          <n-grid cols="2 400:4 600:6 800:7 1000:8" :x-gap="10" :y-gap="30">
            <n-grid-item v-for="(item, index) in dataList" :key="item.fileId" @click="handleFileChecked(index)">
              <div class="file-scale-out-box">
                <div class="file-scale-in-box" @contextmenu="handleFileContextMenu($event, item)">
                  <!-- 图片使用了 七牛云对象存储图片基本处理 缩放 -->
                  <!-- https://developer.qiniu.com/dora/1279/basic-processing-images-imageview2 -->
                  <img
                    v-if="imgExtNmArr.includes(item.fileExtNm)"
                    :src="item.fileUrl + '?imageView2/2/w/200'"
                    loading="lazy"
                    decoding="async"
                    class="file-item"
                  />
                  <video v-else-if="videoExtNmArr.includes(item.fileExtNm)" class="file-item">
                    <source :src="item.fileUrl" :type="item.mimeType" />
                  </video>
                  <div v-else class="other-file-item">
                    <span class="other-file-text">.{{ item.fileExtNm }}</span>
                  </div>
                  <div class="file-tag">{{ item.fileExtNm }}</div>
                  <div class="file-name">
                    <n-ellipsis>{{ item.orgnlNm }}</n-ellipsis>
                  </div>
                  <div v-if="item.checked" class="file-check-icon-box">
                    <n-icon class="file-check-icon" size="30" color="#34d279" :component="CheckCircleFilled" />
                  </div>
                  <div v-if="isOperateFile" class="file-check-box">
                    <n-checkbox size="large" :checked="operateFileIds.includes(item.fileId)" />
                  </div>
                </div>
              </div>
            </n-grid-item>
          </n-grid>
          <div v-if="dataList.length < 1" class="file-empty-box">
            <n-empty description="无数据" style="margin: auto" />
          </div>
          <div v-if="dataLoading" class="load-mask">
            <n-spin class="spin" />
          </div>
        </div>
        <div class="main-foot-box">
          <n-pagination
            v-model:page="pagination.page"
            :item-count="pagination.itemCount"
            :page-size="pagination.pageSize"
            :suffix="pagination.prefix"
            @update:page="loadData()"
          />
        </div>
      </n-grid-item>
    </n-grid>
    <SelectCgy v-model:show="showCgyModal" :file-cgy-tree="fileCgyList" :width="400" @selected="handleSelectedCgy" />
    <CgyEdit v-model:show="showCgyEditModal" :biz-id="cgyBizId" :parent-cgy="parentCgy" :file-tp-cd="fileTpCd" @refresh="loadFileCgyData" />
    <template #action>
      <n-space>
        <n-button @click="() => handleUpdateShow(false)">取消</n-button>
        <n-button type="primary" :loading="selectBtnLoading" @click="handleSelected">
          <template #icon>
            <n-icon>
              <CheckOutlined />
            </n-icon>
          </template>
          确定
        </n-button>
      </n-space>
    </template>
    <n-dropdown
      placement="bottom-start"
      trigger="manual"
      :x="fileContextMenu.x"
      :y="fileContextMenu.y"
      :options="fileContextMenuOptions"
      :show="fileContextMenu.show"
      @clickoutside="handleFileContextMenuClickoutside"
      @select="handleFileContextMenuSelect"
    />
  </n-modal>
</template>

<script lang="ts" setup>
import { ref, reactive, unref, watch, h, nextTick } from 'vue';
import type { PropType } from 'vue';
import { NSpace, NIcon, NButton, NPopconfirm, NPopover } from 'naive-ui';
import type { PaginationInfo, TreeOption, DropdownOption } from 'naive-ui';
import { removeEmptyField, previewImage, formatFileSize } from '@yanfan/admin-layout/utils';
import { fileCgyQry001, fileCgyRmv } from '@yanfan/bmpfl-tsapi/file-cgy';
import type { FileCgyQry001Output_FileCgyDto } from '@yanfan/bmpfl-tsapi/file-cgy/types.d';
import { fileQry, fileRmv001, fileMov } from '@yanfan/bmpfl-tsapi/file';
import type { FileQryOutput_FileDto } from '@yanfan/bmpfl-tsapi/file/types.d';
import { fileTpGet001 } from '@yanfan/bmpfl-tsapi/file-tp';
import type { FileTpGet001Output } from '@yanfan/bmpfl-tsapi/file-tp/types.d';
import { uploadFile002 } from '@/modules/bmpfl/api/file-upload';
import {
  SearchOutlined,
  CheckOutlined,
  DeleteOutlined,
  CloudUploadOutlined,
  PlusOutlined,
  EditOutlined,
  MoreOutlined,
  CheckCircleFilled,
  EyeOutlined,
  LoginOutlined,
} from '@vicons/antd';
import type { OptionalKeys } from '@yanfan/admin-layout/types';
import SelectCgy from './SelectCgy.vue';
import CgyEdit from './CgyEdit.vue';

defineOptions({
  name: 'FileSelect',
});

interface IFormParams {
  keyword: null | string;
  fileTpCd: string;
  fileCgyId: string;
}

interface IFileOptionView extends FileQryOutput_FileDto {
  checked: boolean;
}

type IFileCgyTreeOption = OptionalKeys<FileCgyQry001Output_FileCgyDto, 'children'>;

const props = defineProps({
  show: {
    type: Boolean as PropType<boolean>,
    required: true,
    default: false,
  },
  width: {
    type: Number as PropType<number>,
    default: 1200,
  },
  fileTpCd: {
    type: String as PropType<string>,
    required: true,
    default: '',
  },
  selectCount: {
    type: Number as PropType<number>,
    default: 1,
    validator: (value: number) => value > 0,
  },
});

const emits = defineEmits<{
  (e: 'update:show', show: boolean): void;
  (e: 'selected', value: { checkedFileUrlList: string[] }): void;
}>();

const imgExtNmArr = ['apng', 'avif', 'bmp', 'gif', 'png', 'jpg', 'jpeg', 'webp'];
const videoExtNmArr = ['mp4', 'mpeg', 'ogg', 'mp2t', 'webm', '3gpp', '3gpp2'];

const showModal = ref<boolean>(false);
const currFileTp = ref<FileTpGet001Output | null>(null);
const formParams = ref(defaultFormParams());
const selectBtnLoading = ref<boolean>(false);
const dataLoading = ref<boolean>(false);
const fileCgyloading = ref<boolean>(false);
const isOperateFile = ref<boolean>(false);
const operateFileIds = ref<string[]>([]);
const pagination = reactive({
  page: 1,
  pageSize: 28,
  itemCount: 0,
  prefix({ itemCount }: PaginationInfo) {
    return `共${itemCount}条`;
  },
});
const dataList = ref<IFileOptionView[]>([]);
const showCgyEditModal = ref<boolean>(false);
const showCgyModal = ref<boolean>(false);
const cgyBizId = ref<string>('');
const parentCgy = ref<IFileCgyTreeOption | null>(null);
const fileCgyList = ref<IFileCgyTreeOption[]>([]);
const upFileloading = ref<boolean>(false);
const delBatchLoading = ref<boolean>(false);
const moveBatchLoading = ref<boolean>(false);

const fileContextMenuOptions: DropdownOption[] = [
  {
    label: '预览',
    key: 'view',
    icon: () => h(NIcon, { size: 18 }, () => h(EyeOutlined)),
  },
  {
    label: '移动',
    key: 'move',
    icon: () => h(NIcon, { size: 18 }, () => h(LoginOutlined)),
  },
  {
    label: '删除',
    key: 'del',
    icon: () => h(NIcon, { size: 18 }, () => h(DeleteOutlined)),
  },
];
const fileContextMenu = reactive<{ show: boolean; x: number; y: number; fileInfo: IFileOptionView | null }>({
  show: false,
  x: 0,
  y: 0,
  fileInfo: null,
});

watch(
  () => unref(props).show,
  (show) => {
    showModal.value = show;
    if (showModal.value) {
      formParams.value = defaultFormParams();
      formParams.value.fileTpCd = props.fileTpCd;
      pagination.page = 1;
      loadFileTp();
      loadFileCgyData();
      loadData();
    } else {
      fileCgyList.value = [];
      dataList.value = [];
    }
  },
);
function handleUpdateShow(value: boolean) {
  emits('update:show', value);
}

function defaultFormParams(): IFormParams {
  return {
    keyword: null,
    fileTpCd: '',
    fileCgyId: '',
  };
}

function loadData() {
  operateFileIds.value = [];
  isOperateFile.value = false;
  dataLoading.value = true;
  fileQry({ pageIndx: pagination.page, pageSize: pagination.pageSize, ...removeEmptyField(formParams.value) })
    .then((data) => {
      dataList.value = data.list.map((item) => ({ ...item, checked: false }));
      pagination.itemCount = data.totl;
    })
    .finally(() => {
      dataLoading.value = false;
    });
}

function loadFileTp() {
  return fileTpGet001({ fileTpCd: props.fileTpCd }).then((data) => {
    currFileTp.value = data;
  });
}

function handleSearch() {
  pagination.page = 1;
  loadData();
}

function handleReset() {
  formParams.value = defaultFormParams();
  formParams.value.fileTpCd = props.fileTpCd;
}

function renderFileCgyTreeSuffix({ option }: { option: TreeOption }) {
  const row = option as IFileCgyTreeOption;
  return h(
    NPopover,
    {},
    {
      trigger: () => h(NIcon, { size: 18 }, () => h(MoreOutlined)),
      default: () =>
        h(
          'div',
          { style: { display: 'flex', flexDirection: 'column' } },
          {
            default: () => [
              h(
                NButton,
                { type: 'info', text: true, disabled: row.fileCgyId === 'UNGROUPED', onClick: () => handleFileCgyAdd(row) },
                { icon: () => h(NIcon, { size: 18 }, () => h(PlusOutlined)), default: () => '添加' },
              ),
              h(
                NButton,
                { type: 'info', text: true, disabled: row.fileCgyId === 'UNGROUPED', onClick: () => handleFileCgyEdit(row) },
                { icon: () => h(NIcon, { size: 18 }, () => h(EditOutlined)), default: () => '编辑' },
              ),
              h(
                NPopconfirm,
                { onPositiveClick: () => handleFileCgyDelete(row) },
                {
                  trigger: () =>
                    h(
                      NButton,
                      { type: 'error', text: true, disabled: row.fileCgyId === 'UNGROUPED' },
                      { icon: () => h(NIcon, { size: 18 }, () => h(DeleteOutlined)), default: () => '删除' },
                    ),
                  default: () => '确定要删除该数据吗？',
                },
              ),
            ],
          },
        ),
    },
  );
}

function handleCgySelectChange(_keys: string[], rows: Array<TreeOption | null>) {
  handleFileCgyClick(rows[0] as IFileCgyTreeOption);
}

function handleFileCgyAdd(row: IFileCgyTreeOption | null) {
  parentCgy.value = row;
  cgyBizId.value = '';
  showCgyEditModal.value = true;
}

function handleFileCgyEdit(row: IFileCgyTreeOption) {
  cgyBizId.value = row.fileCgyId;
  parentCgy.value = null;
  showCgyEditModal.value = true;
}

function handleFileCgyDelete(row: IFileCgyTreeOption) {
  fileCgyRmv({ fileCgyId: row.fileCgyId }).then(() => {
    window.$message.success('删除成功');
    loadFileCgyData();
  });
}

function handleFileContextMenu(event: MouseEvent, item: IFileOptionView) {
  event.preventDefault();
  fileContextMenu.show = false;
  nextTick(() => {
    fileContextMenu.show = true;
    fileContextMenu.x = event.clientX;
    fileContextMenu.y = event.clientY;
    fileContextMenu.fileInfo = item;
  });
}

function handleFileContextMenuClickoutside() {
  fileContextMenu.show = false;
}

function handleFileContextMenuSelect(key: string) {
  switch (key) {
    case 'view':
      if (!fileContextMenu.fileInfo?.fileUrl) {
        window.$message.error('文件地址无效');
        return;
      }
      previewImage([fileContextMenu.fileInfo.fileUrl], 0);
      break;
    case 'move':
      showCgyModal.value = true;
      break;
    case 'del':
      handleDelFile();
      break;
  }
  fileContextMenu.show = false;
}

function handleDelFile() {
  if (!fileContextMenu.fileInfo?.fileId) {
    window.$message.error('文件无效');
    return;
  }
  const fileIdList = [fileContextMenu.fileInfo.fileId];
  const delDialog = window.$dialog.warning({
    title: '删除文件',
    content: '确定要删除该文件吗？',
    positiveText: '确定',
    negativeText: '取消',
    onPositiveClick: () => {
      delDialog.loading = true;
      return fileRmv001({ fileIdList })
        .then(() => {
          window.$message.success('删除成功');
          loadData();
        })
        .finally(() => {
          delDialog.loading = false;
        });
    },
  });
}

async function handleUpload() {
  if (!currFileTp.value) {
    upFileloading.value = true;
    await loadFileTp();
    upFileloading.value = false;
  }
  const upInput = document.createElement('input');
  upInput.type = 'file';
  upInput.multiple = true;
  if (currFileTp.value?.imgFlg) {
    upInput.accept = ['image/jpeg', 'image/png', 'image/webp'].join(',');
  }
  upInput.addEventListener('change', async (event) => {
    const files = (event.target as any).files as FileList;
    if (files.length > 10) {
      window.$message.error('每次上传最多选择10个文件');
      return;
    }
    upFileloading.value = true;
    const upFileList: File[] = [];
    for (const file of files) {
      //验证文件类型
      if (currFileTp.value?.extNmAry) {
        const extNmArr = currFileTp.value.extNmAry.split(',');
        let fileExtName = getFileExtension(file.name);
        if (['image/jpeg', 'image/png'].includes(file.type)) {
          fileExtName = 'webp';
        }
        if (!extNmArr.includes(fileExtName)) {
          window.$message.error(`仅支持${currFileTp.value.extNmAry}类型文件上传`);
          upFileloading.value = false;
          return;
        }
      }
      //验证大小
      const validateRes = fileSizeValidate(file);
      if (!validateRes.result) {
        window.$message.error(validateRes.msg);
        upFileloading.value = false;
        return;
      }
      //验证图片文件高宽
      if (/^image.*/.test(file.type)) {
        const widthAndHeightValidateRes = await imgWidthAndHeightValidate(file);
        if (!widthAndHeightValidateRes.result) {
          window.$message.error(widthAndHeightValidateRes.msg);
          upFileloading.value = false;
          return;
        }
      }
      //尝试将jpg和png图片转换成webp图片
      if (['image/jpeg', 'image/png'].includes(file.type)) {
        const webpImgFile = await tryImgToWebp(file);
        upFileList.push(webpImgFile);
      } else {
        upFileList.push(file);
      }
    }
    uploadFile002({ fileTpCd: formParams.value.fileTpCd, fileCgyId: formParams.value.fileCgyId }, upFileList)
      .then((res) => {
        if (res.errcode === '0') {
          window.$message.success('上传成功');
        } else {
          window.$message.error(res?.errmsg || '上传失败');
        }
        loadData();
      })
      .finally(() => {
        upFileloading.value = false;
        upInput.value = '';
      });
  });
  upInput.click();
}
//验证文件大小
function fileSizeValidate(file: File): { result: boolean; msg: string } {
  const fielTpInfo = currFileTp.value as FileTpGet001Output;
  if (file.size > fielTpInfo.maxSize) {
    return { result: false, msg: `${file.name}超过${formatFileSize(fielTpInfo.maxSize)}` };
  }
  if (file.size < fielTpInfo.minSize) {
    return { result: false, msg: `${file.name}小于${formatFileSize(fielTpInfo.minSize)}` };
  }
  return { result: true, msg: '' };
}

//验证图片文件高宽
function imgWidthAndHeightValidate(file: File): Promise<{ result: boolean; msg: string }> {
  const fielTpInfo = currFileTp.value as FileTpGet001Output;
  return new Promise((resolve) => {
    const img = new Image();
    const url = URL.createObjectURL(file);
    img.addEventListener('load', () => {
      const width = img.naturalWidth;
      const height = img.naturalHeight;
      if (width > fielTpInfo.maxWidth) {
        resolve({ result: false, msg: `${file.name}宽超过${fielTpInfo.maxWidth}px` });
        return;
      }
      if (width < fielTpInfo.minWidth) {
        resolve({ result: false, msg: `${file.name}宽小于${fielTpInfo.minWidth}px` });
        return;
      }
      if (height > fielTpInfo.maxHigt) {
        resolve({ result: false, msg: `${file.name}高超过${fielTpInfo.maxHigt}px` });
        return;
      }
      if (height < fielTpInfo.minHigt) {
        resolve({ result: false, msg: `${file.name}高小于${fielTpInfo.minHigt}px` });
        return;
      }
      resolve({ result: true, msg: '' });
    });
    img.src = url;
  });
}

//尝试将图片格式转为webp
function tryImgToWebp(file: File): Promise<File> {
  return new Promise((resolve) => {
    const objUrl = URL.createObjectURL(file);
    const img = new Image();
    img.addEventListener('load', () => {
      const canvas = document.createElement('canvas');
      canvas.width = img.width;
      canvas.height = img.height;
      const ctx = canvas.getContext('2d');
      if (!ctx) {
        resolve(file);
        return;
      }
      ctx.drawImage(img, 0, 0);
      canvas.toBlob((blob) => {
        if (blob) {
          const fileName = file.name.replace(/\.[0-9a-z]+$/, '.webp');
          resolve(new File([blob], fileName, { type: 'image/webp' }));
        } else {
          resolve(file);
        }
      }, 'image/webp');
    });
    img.src = objUrl;
  });
}

//获取文件扩展名
function getFileExtension(filename: string): string {
  return filename.slice(((filename.lastIndexOf('.') - 1) >>> 0) + 2);
}

function handleFileCgyClick(fileCgy: IFileCgyTreeOption) {
  // formParams.value.fileTpCd = fileCgy.fileTpCd;
  formParams.value.fileCgyId = fileCgy.fileCgyId;
  pagination.page = 1;
  loadData();
}

function loadFileCgyData() {
  fileCgyloading.value = true;
  fileCgyQry001({ fileTpCd: formParams.value.fileTpCd })
    .then((data) => {
      handleLeafNode(data.list);
      fileCgyList.value = data.list;
    })
    .finally(() => {
      fileCgyloading.value = false;
    });
}

function handleLeafNode(nodes: IFileCgyTreeOption[] | undefined) {
  if (nodes) {
    nodes.map((node) => {
      if (node.children && node.children.length === 0) {
        // node.isLeaf = true;
        node.children = undefined;
      } else {
        handleLeafNode(node.children);
      }
    });
  }
}

function handleClickOpFile() {
  dataList.value.forEach((item) => (item.checked = false));
  operateFileIds.value = [];
  isOperateFile.value = !isOperateFile.value;
}

function handleClickBatchMove() {
  if (operateFileIds.value.length < 1) {
    window.$message.error('请至少选中一个文件');
    return;
  }
  showCgyModal.value = true;
}

function handleSelectedCgy(fileCgyId: string) {
  let fileIdList: string[] = [];
  if (isOperateFile.value) {
    fileIdList = operateFileIds.value;
  } else if (fileContextMenu.fileInfo?.fileId) {
    fileIdList = [fileContextMenu.fileInfo.fileId];
  }
  moveBatchLoading.value = true;
  fileMov({ fileCgyId: fileCgyId, fileIdList })
    .then(() => {
      operateFileIds.value = [];
      window.$message.success('移动成功');
      loadData();
    })
    .finally(() => {
      moveBatchLoading.value = false;
    });
}

function handleClickBatchDelFile() {
  if (operateFileIds.value.length < 1) {
    window.$message.error('请至少选中一个文件');
    return;
  }
  delBatchLoading.value = true;
  fileRmv001({ fileIdList: operateFileIds.value })
    .then(() => {
      operateFileIds.value = [];
      window.$message.success('删除成功');
      loadData();
    })
    .finally(() => {
      delBatchLoading.value = false;
    });
}

function handleSelected() {
  const checkedFileUrlList = dataList.value.filter((file) => file.checked).map((file) => file.fileUrl);
  if (checkedFileUrlList.length > 0) {
    emits('selected', { checkedFileUrlList });
    handleUpdateShow(false);
  } else {
    window.$message.error('请选择文件！');
  }
}

function handleFileChecked(index: number) {
  if (isOperateFile.value) {
    const currFile = dataList.value[index];
    const idIndex = operateFileIds.value.findIndex((fileId) => fileId === currFile.fileId);
    if (idIndex > -1) {
      operateFileIds.value.splice(idIndex, 1);
    } else {
      operateFileIds.value.push(currFile.fileId);
    }
  } else {
    if (props.selectCount < 1) {
      window.$message.error('可选择数量小于1');
      return;
    } else if (props.selectCount > 1) {
      const currSelectedCount = dataList.value.reduce((pre, cur) => (cur.checked ? pre + 1 : pre), 0);
      const currFile = dataList.value[index];
      if (!currFile.checked && currSelectedCount >= props.selectCount) {
        window.$message.error('已经到达最大选择数量');
        return;
      }
    }
    if (props.selectCount === 1) {
      dataList.value.forEach((file) => {
        file.checked = false;
      });
    }
    dataList.value[index].checked = !dataList.value[index].checked;
  }
}
</script>

<style lang="scss" scoped>
.side-head-box {
  display: flex;
  flex-direction: row;
}
.side-body-box {
  flex: 1;
  min-height: 0;
  position: relative;
  .load-mask {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-color: rgba(0, 0, 0, 0.1);
    display: flex;
    .spin {
      margin: auto;
    }
  }
}
.main-head-box {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
}
.main-body-box {
  flex: 1;
  min-height: 500px;
  border: 1px solid #efeff5;
  border-radius: 5px;
  padding: 10px 10px 30px 10px;
  overflow-y: auto;
  position: relative;
  .file-scale-out-box {
    width: 100%;
    padding-top: 100%;
    position: relative;
    cursor: pointer;
    border: 1px solid #dddddd;
    border-radius: 5px;
    .file-scale-in-box {
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      .file-item {
        width: 100%;
        height: 100%;
        object-fit: contain;
        transform: translate3d(0, 0, 0); // 开启硬件加速否则页面会因为设置object-fit: cover;导致很卡
      }
      .other-file-item {
        width: 100%;
        height: 100%;
        display: flex;
        background-color: #fafafa;
        .other-file-text {
          margin: auto;
          font-size: 24px;
        }
      }
      .file-tag {
        position: absolute;
        top: 2px;
        right: 2px;
        padding: 2px 5px;
        border-radius: 5px;
        background-color: rgba(179, 179, 179, 0.9);
        color: #ffffff;
        font-size: 12px;
      }
      .file-check-icon-box {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        background-color: rgba(0, 0, 0, 0.5);
        display: flex;
        .file-check-icon {
          margin: auto;
        }
      }
      .file-check-box {
        position: absolute;
        bottom: 2px;
        right: 2px;
      }
    }
  }
  .file-empty-box {
    height: 100%;
    display: flex;
  }
  .load-mask {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-color: rgba(0, 0, 0, 0.1);
    display: flex;
    .spin {
      margin: auto;
    }
  }
}
.main-foot-box {
  display: flex;
  flex-direction: row;
  justify-content: flex-end;
}
</style>
